package userserv

import (
	"codedef"
	"config"
	"dev"
	"encoding/json"
	"fmt"
	"github.com/garyburd/redigo/redis"
	"log"
	"net/http"
	"rhelper"
	"time"
)

type ControllerInfo struct {
	Id    string `json:"id"`
	Alias string `json:"alias"`
}

type UserRequest struct {
	Cid        string `json:"cid"`
	UserServer string `json:"userServer"`
	ServerCmd  string `json:"serverCmd"`
	MsgBuf     []byte `json:"MsgBuf"`
}

type Controller struct {
	Id      string `json:"id"`
	CType   string `json:"type"`
	AppType string `json:"apptype"`

	DeviceName        string `json:"deviceName"`
	DeviceVersion     string `json:"deviceVersion"`
	DeviceVersionCode string `json:"deviceVersionCode"`

	FirmName        string `json:"firmName"`
	FirmVersion     string `json:"firmVersion"`
	FirmVersionCode string `json:"firmVersionCode"`

	Status string `json:"status"`

	Alias         string `json:"alias"`
	Remark        string `json:"remark"`
	MessageNotify string `json:"messageNotify"`

	Account  string `json:"account"`
	CtlFlags string `json:"ctlFlags"`

	LatestFirmVersion     string `json:"latestFirmVersion"`
	LatestFirmVersionCode int    `json:"latestFirmVersionCode"`
	Relay                 string `json:"relay"`

	IsBindCloseTime int `json:"isBindCloseTime"`
	CloseTime       int `json:"closeTime"`

	LeftOnTime   int `json:"leftOnTime"`
	LeftOnEnable int `json:"leftOnEnable"`

	ImgName string `json:"imgName"`

	ServerId string

	DeviceModel       string `json:"deviceModel"`
	Brand             string `json:"brand"`
	HasNewFirmVersion int    `json:"hasNewFirmVersion"`
}

type DevAction struct {
	ActionId   int      `json:"actionId"`
	ActionName string   `json:"actionName"`
	ActionArg  []string `json:"actionArg"`
	ActionTime int      `json:"actionTime"`
	Cid        string   `json:"cid"`
}

// func giveupWaitAndDiscard(cid string) {
// 	dev.GiveupWaitAndDiscard(cid)
// }

// func isControllerBusy(cid string) bool {
// 	return dev.IsControllerBusy(cid)
// }

func generateActionId() (int, error) {
	rconn := rhelper.Pool().Get()
	defer rconn.Close()

	id, err := redis.Int(rconn.Do("INCR", rhelper.ActionHolder))
	return id, err
}

func GetRuntimeInfo(w http.ResponseWriter,
	controller *Controller) *RuntimeInfo {

	type GetRuntime struct {
		Uri string `json:"uri"`
	}

	msg := GetRuntime{}
	msg.Uri = "/getRuntime"

	if wait := syn2Device(*controller, msg); wait != nil {
		devMessage, errCode := parserDevReply(wait)
		if 0 != errCode {
			replyError(w, codedef.ERR_OPRATION_FAILD)
			return nil
		}

		var runTimeInfo RuntimeInfo
		err := json.Unmarshal(devMessage, &runTimeInfo)
		if err != nil {
			log.Println("get runtime info failed, json decode failed:", err)
			replyError(w, codedef.ERR_DECODE)
			return nil
		}

		return &runTimeInfo
	} else {
		log.Println("get runtime info failed, syn2Device error")
		replyError(w, codedef.ERR_CONTROLLER_NO_RESPONSE)
		return nil
	}
}

func syn2Device(controller Controller, action interface{}) []byte {
	wait := make(chan []byte, 1)
	controller.writeJSONWait(action, wait)
	select {
	case reply := <-wait:
		return reply
	case <-time.After(time.Second * 120):
		controller.giveupWaitAndDiscard()
		log.Println("syncPlan2Device timeout")
		break
	}
	//return codedef.ERR_SUCCEED
	return nil
}

func parserDevReply(buf []byte) ([]byte, int) {
	type ReplyMsg struct {
		ErrCode int    `json:"errCode"`
		MsgBuf  []byte `json:"msgBuf"`
	}

	replyMsg := ReplyMsg{}
	err := json.Unmarshal(buf, &replyMsg)
	if err != nil {
		log.Println("onDevRequest failed, json decode failed:", err)
		return nil, codedef.ERR_DECODE
	}

	if replyMsg.ErrCode != 0 {
		return nil, replyMsg.ErrCode
	}

	return replyMsg.MsgBuf, 0

}

func deleteWaitKey(account string) {
	dev.DeleteWaitKey(account)

	rconn := rhelper.Pool().Get()
	defer rconn.Close()
	rconn.Do("DEL", rhelper.WaitKey+account)
}

func setWaitKey(account string, waitKey chan int, waitCid chan string) {
	dev.SetWaitKey(account, waitKey, waitCid)
}

func getWaitkey(account string) (chan int, bool) {
	return dev.GetWaitKey(account)
}

func getOnlineControllerById(cid string) *Controller {
	rconn := rhelper.Pool().Get()
	defer rconn.Close()

	fields, _ := redis.Strings(rconn.Do("HMGET", rhelper.ControllerHash+cid, "serverId", "relay", "account", "firmVersion", "firmVersionCode"))
	if fields[0] == "" && fields[1] == "" && fields[2] == "" {
		log.Println("getOnlineControllerById device is offline, id:", cid)
		return nil
	}

	c := Controller{}
	c.Id = cid
	c.ServerId = fields[0]
	c.Relay = fields[1]
	c.Account = fields[2]
	c.FirmVersion = fields[3]
	c.FirmVersionCode = fields[4]

	if c.ServerId == "" {
		return nil
	}

	//exist, err := redis.Int(rconn.Do("SISMEMBER", rhelper.OnlineDev+c.ServerId, cid))
	//if err != nil {
	//	log.Println("getOnlineControllerById err:", err)
	//}
	//
	//if exist != 1 {
	//	return nil
	//}

	return &c
}

func IsControllerOnline(serverId string, cid string) bool {
	return isControllerOnline(serverId, cid)
}

// check controller on the server if online
func isControllerOnline(serverId string, cid string) bool {
	if serverId == "" {
		return false
	}

	if cid == "" {
		return false
	}

	rconn := rhelper.Pool().Get()
	defer rconn.Close()
	exist, err := redis.Int(rconn.Do("SISMEMBER", rhelper.OnlineDev, rhelper.ControllerHash+cid))
	if err != nil {
		log.Println("isControllerOnline err:", err)
	}

	if exist != 1 {
		return false
	}

	return true
}

func updateControllerRelay(cid string, relay string) {
	rconn := rhelper.Pool().Get()
	defer rconn.Close()

	rconn.Do("HSET", rhelper.ControllerHash+cid, "relay", relay)
}

// func (c *Controller) updateOnlineControllerRelay(relay string) {
// 	rconn := rhelper.Pool().Get()
// 	defer rconn.Close()

// 	rconn.Do("HSET", rhelper.ControllerHash+c.Id, "relay", relay)
// }

func (c *Controller) giveupWaitAndDiscard() {
	dev.GiveupWaitAndDiscard(c.Id)
}

func (c *Controller) isBusy() bool {
	return dev.IsControllerBusy(c.Id)
}

func (c *Controller) closeDevWebSocket() error {
	if c.isBusy() {
		return fmt.Errorf("controller is busy now")
	}

	userRequest := UserRequest{}
	userRequest.Cid = c.Id
	userRequest.UserServer = config.ServerId
	userRequest.ServerCmd = "closeWebSocket"

	buf, err := json.Marshal(userRequest)
	if err != nil {
		log.Println("OnUserRequest failed, json encode failed:", err)
		return err
	}

	publish(c.ServerId, buf)

	return nil

}

func (c *Controller) onUserRequestWait(message []byte, wait chan<- []byte) error {
	if c.isBusy() {
		return fmt.Errorf("controller is busy now")
	}

	dev.SetWaitDevRsp(c.Id, wait)

	userRequest := UserRequest{}
	userRequest.Cid = c.Id
	userRequest.UserServer = config.ServerId
	userRequest.MsgBuf = message

	buf, err := json.Marshal(userRequest)
	if err != nil {
		log.Println("OnUserRequest failed, json encode failed:", err)
		return err
	}

	publish(c.ServerId, buf)

	return nil
}

func (c *Controller) writeJSONWait(j interface{}, wait chan<- []byte) error {
	if c.isBusy() {
		return fmt.Errorf("controller is busy now")
	}

	dev.SetWaitDevRsp(c.Id, wait)

	msgBuf, err := json.Marshal(j)
	if err != nil {
		log.Println("writeJSONWait, encode error")
		return err
	}

	userRequest := UserRequest{}
	userRequest.Cid = c.Id
	userRequest.UserServer = config.ServerId
	userRequest.MsgBuf = msgBuf

	buf, err := json.Marshal(userRequest)
	if err != nil {
		log.Println("WriteJSONWait failed, json encode failed:", err)
		return err
	}

	publish(c.ServerId, buf)

	return nil
}

func publish(channel, value interface{}) {
	rconn := rhelper.Pool().Get()
	defer rconn.Close()
	//log.Println("PUBLISH", channel, string(value.([]byte)))
	rconn.Do("PUBLISH", channel, value)
}

func checkDevCtrlAction(cid string, uid string) (errCode int) {

	if cid == "" {
		log.Println("checkDevCtrlAction failed, invalid request param")
		errCode = codedef.ERR_REQUEST_INVALID_PARAM
		return
	}

	/*if !isUserDeviceExist(uid, cid) {
		log.Println("checkDevCtrlAction failed, controller not exist ", cid)
		errCode = codedef.ERR_CONTROLLER_NOT_EXIST
		return
	}
	*/
	/*if !isAppCountDownSupport(uid) {
		log.Println("onCreateCountDown failed, app version is lower than min support version, uniqueId: ", uid)
		errCode = codedef.ERR_APP_MODULE_SMALL_THEN_MIN_SUPPORT
		return
	}

	if !isFirmwareCountDownSupport(cid) {
		log.Println("onCreateCountDown failed, firmware version is lower than min support version, device id: ", cid)
		errCode = codedef.ERR_FIRMWARE_MODULE_SMALL_THEN_MIN_SUPPORT
		return
	}*/
	errCode = codedef.ERR_SUCCEED
	return
}
